package com.hotlove.tools.common;

import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Auther: hotlove_linx
 * @Date: 2022/4/16 18:01
 * @Description:
 */
public class CommonUtils {

    public static String escapeJavaScript(String str) {
        if (str == null) {
            return null;
        }

        StringBuffer writer = new StringBuffer(str.length() * 2);

        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            char ch = str.charAt(i);

            // handle unicode
            if (ch > 0xfff) {
                writer.append("\\u");
                writer.append(hex(ch));
            } else if (ch > 0xff) {
                writer.append("\\u0");
                writer.append(hex(ch));
            } else if (ch > 0x7f) {
                writer.append("\\u00");
                writer.append(hex(ch));
            } else if (ch < 32) {
                switch (ch) {
                    case '\b':
                        writer.append('\\');
                        writer.append('b');
                        break;
                    case '\n':
                        writer.append('\\');
                        writer.append('n');
                        break;
                    case '\t':
                        writer.append('\\');
                        writer.append('t');
                        break;
                    case '\f':
                        writer.append('\\');
                        writer.append('f');
                        break;
                    case '\r':
                        writer.append('\\');
                        writer.append('r');
                        break;
                    default:
                        if (ch > 0xf) {
                            writer.append("\\u00");
                            writer.append(hex(ch));
                        } else {
                            writer.append("\\u000");
                            writer.append(hex(ch));
                        }
                        break;
                }
            } else {
                switch (ch) {
                    case '\'':
                        // If we wanted to escape for Java strings then we would
                        // not need this next line.
                        writer.append('\\');
                        writer.append('\'');
                        break;
                    case '"':
                        writer.append('\\');
                        writer.append('"');
                        break;
                    case '\\':
                        writer.append('\\');
                        writer.append('\\');
                        break;
                    default:
                        writer.append(ch);
                        break;
                }
            }
        }

        return writer.toString();
    }

    /**
     * <p>
     * Returns an upper case hexadecimal <code>String</code> for the given
     * character.
     * </p>
     *
     * @param ch
     *            The character to convert.
     * @return An upper case hexadecimal <code>String</code>
     */
    private static String hex(char ch) {
        return Integer.toHexString(ch).toUpperCase();
    }

    public static String hex(String str, String charset) throws UnsupportedEncodingException {
        return bytes2Hex(str.getBytes(charset));
    }

    public static String hex2String(String hex, String charset) throws UnsupportedEncodingException {
        byte[] baKeyword = new byte[hex.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        hex = new String(baKeyword, charset);

        return hex;
    }

    public static String bytes2Hex(byte[] bts) {
        StringBuffer des = new StringBuffer();
        String tmp = null;
        for (int i = 0; i < bts.length; i++) {
            tmp = (Integer.toHexString(bts[i] & 0xFF));
            if (tmp.length() == 1) {
                des.append('0');
            }
            des.append(tmp);
        }
        return des.toString();
    }

    /**
     * 将表示16进制值的字符串转换为byte数组， 和bytes2Hex 互为可逆的转换过程
     *
     * @param strIn
     *            需要转换的字符串
     * @return 转换后的byte数组
     * @throws Exception
     *             本方法不处理任何异常，所有异常全部抛出
     */
    public static byte[] hex2Bytes(String strIn) {
        byte[] arrB = strIn.getBytes();
        int iLen = arrB.length;

        // 两个字符表示一个字节，所以字节数组长度是字符串长度除以2
        byte[] arrOut = new byte[iLen / 2];
        for (int i = 0; i < iLen; i = i + 2) {
            String strTmp = new String(arrB, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
        }
        return arrOut;
    }

    public static List<Long> toList(long[] array) {

        List<Long> list = new ArrayList<Long>();

        for (long l : array) {
            list.add(l);
        }

        return list;
    }

    /**
     * Currency format
     */
    public static final DecimalFormat cdf = new DecimalFormat("0.000");

    /**
     * 将一组对象toString后用逗号分隔
     *
     * @param objects
     * @return
     */
    public static String toStrings(Object... objects) {
        return StringUtils.arrayToCommaDelimitedString(objects);
    }

    /**
     * id值是否有效，有效的id是指大于0的整数。
     *
     * @param id
     * @return
     */
    public static boolean isIdValid(Long id) {
        return null != id && 0 < id;
    }

    /**
     * 将字符串类型的整数转换为Long型
     *
     * @param s
     * @return
     */
    public static Long parseLong(String s) {
        try {
            return Long.parseLong(s);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 将逗号分隔的ID字符串转换为List
     *
     * @param str
     * @return
     */
    public static List<Long> commaDelimitedStringToLongList(String str) {
        List<Long> list = new ArrayList<Long>();

        if (!StringUtils.hasText(str)) {
            return list;
        }

        for (String string : str.split(",")) {
            Long longValue = Long.valueOf(string);
            list.add(longValue);
        }

        return list;
    }

    /**
     * 批量转换String 到 Long类型
     *
     * @param str
     * @return
     */
    public static long[] stringArrayToLongArray(String[] str) {

        long[] longs = new long[str.length];

        for (int i = 0; i < str.length; i++) {
            longs[i] = Long.parseLong(str[i]);
        }

        return longs;

    }


    private static final Pattern DURATION_PATTERN = Pattern.compile("^(-)?\\d+(D|d|Y|y|M|m)$");

    public static boolean isDurationValid(String duration) {
        Matcher matcher = DURATION_PATTERN.matcher(duration);

        return matcher.matches();
    }

    /**
     * 根据自定义的期限字符串对日期进行操作。 可接受的期限格式为：nd/-nd/nM/-nM/nY/-nY,n为整数
     *
     * @param date
     * @param duration
     * @return
     */
    public static Date addDuration(Date date, String duration) {
        if (!isDurationValid(duration)) {
            return date;
        }

        duration = duration.toUpperCase();

        char c = duration.charAt(duration.length() - 1);
        int amount = Integer.parseInt(duration.substring(0, duration.length() - 1));

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        switch (c) {
            case 'Y':
                calendar.add(Calendar.YEAR, amount);
                break;
            case 'M':
                calendar.add(Calendar.MONTH, amount);
                break;
            case 'D':
                calendar.add(Calendar.DATE, amount);
                break;
            default:
                break;
        }

        return calendar.getTime();
    }

    /**
     * 截取根据字符宽度截取字符串，ascii字符宽度为1，汉字宽度为2
     *
     * @param value
     * @param size
     * @return
     */
    public static String truncateString(String value, int size) {
        if (null == value)
            return "";
        value = value.trim();
        if (length(value) <= size)
            return value;
        return value.substring(0, length(value, size));
    }

    private static int length(String value, int size) {
        int valueLength = 0;
        String chinese = "[\u0391-\uFFE5]";
        int length = value.length();
        // 获取字段值的长度，如果含中文字符，则每个中文字符长度为2，否则为1
        while (size > 0 && valueLength < length) {
            String temp = value.substring(valueLength, valueLength + 1);
            // 判断是否为中文字符
            if (temp.matches(chinese)) {
                size -= 2;
            } else {
                size -= 1;
            }
            valueLength++;
        }
        return valueLength;
    }

    public static int length(String value) {
        int valueLength = 0;
        String chinese = "[\u0391-\uFFE5]";
        /* 获取字段值的长度，如果含中文字符，则每个中文字符长度为2，否则为1 */
        for (int i = 0; i < value.length(); i++) {
            /* 获取一个字符 */
            String temp = value.substring(i, i + 1);
            /* 判断是否为中文字符 */
            if (temp.matches(chinese)) {
                /* 中文字符长度为2 */
                valueLength += 2;
            } else {
                /* 其他字符长度为1 */
                valueLength += 1;
            }
        }
        return valueLength;
    }

    private static final Pattern AMOUNT_PATTERN = Pattern.compile("^-?\\d+(\\.\\d{1,3})?$");

    public static boolean isAmount(String str) {
        Matcher matcher = AMOUNT_PATTERN.matcher(str);

        return matcher.matches();
    }


    /**
     * <p>
     * 判断18位身份证的合法性
     * </p>
     * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定，公民身份号码是特征组合码，由十七位数字本体码和一位数字校验码组成。
     * 排列顺序从左至右依次为：六位数字地址码，八位数字出生日期码，三位数字顺序码和一位数字校验码。
     * <p>
     * 顺序码: 表示在同一地址码所标识的区域范围内，对同年、同月、同 日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配 给女性。
     * </p>
     * <p>
     * 1.前1、2位数字表示：所在省份的代码； 2.第3、4位数字表示：所在城市的代码； 3.第5、6位数字表示：所在区县的代码；
     * 4.第7~14位数字表示：出生年、月、日； 5.第15、16位数字表示：所在地的派出所的代码；
     * 6.第17位数字表示性别：奇数表示男性，偶数表示女性；
     * 7.第18位数字是校检码：也有的说是个人信息码，一般是随计算机的随机产生，用来检验身份证的正确性。校检码可以是0~9的数字，有时也用x表示。
     * </p>
     * <p>
     * 第十八位数字(校验码)的计算方法为： 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为：7 9 10 5 8 4
     * 2 1 6 3 7 9 10 5 8 4 2
     * </p>
     * <p>
     * 2.将这17位数字和系数相乘的结果相加。
     * </p>
     * <p>
     * 3.用加出来和除以11，看余数是多少？
     * </p>
     * 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3
     * 2。
     * <p>
     * 5.通过上面得知如果余数是2，就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10，身份证的最后一位号码就是2。
     * </p>
     *
     * @param idcard
     * @return
     */
    public static boolean isValidate18Idcard(String idcard) {
        // 非18位为假
        if (idcard.length() != 18) {
            return false;
        }
        // 获取前17位
        String idcard17 = idcard.substring(0, 17);
        // 获取第18位
        String idcard18Code = idcard.substring(17, 18);
        char c[] = null;
        String checkCode = "";
        // 是否都为数字
        if (isDigital(idcard17)) {
            c = idcard17.toCharArray();
        } else {
            return false;
        }

        if (null != c) {
            int bit[] = new int[idcard17.length()];

            bit = converCharToInt(c);

            int sum17 = 0;

            sum17 = getPowerSum(bit);

            // 将和值与11取模得到余数进行校验码判断
            checkCode = getCheckCodeBySum(sum17);
            if (null == checkCode) {
                return false;
            }
            // 将身份证的第18位与算出来的校码进行匹配，不相等就为假
            if (!idcard18Code.equalsIgnoreCase(checkCode)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 数字验证
     *
     * @param str
     * @return
     */
    public static boolean isDigital(String str) {
        return str == null || "".equals(str) ? false : str.matches("^[0-9]*$");
    }

    /**
     * 将字符数组转为整型数组
     *
     * @param c
     * @return
     * @throws NumberFormatException
     */
    private static int[] converCharToInt(char[] c) throws NumberFormatException {
        int[] a = new int[c.length];
        int k = 0;
        for (char temp : c) {
            a[k++] = Integer.parseInt(String.valueOf(temp));
        }
        return a;
    }

    /**
     * 将身份证的每位和对应位的加权因子相乘之后，再得到和值
     *
     * @param bit
     * @return
     */
    private static int getPowerSum(int[] bit) {

        int sum = 0;

        if (power.length != bit.length) {
            return sum;
        }

        for (int i = 0; i < bit.length; i++) {
            for (int j = 0; j < power.length; j++) {
                if (i == j) {
                    sum = sum + bit[i] * power[j];
                }
            }
        }
        return sum;
    }

    /**
     * 将和值与11取模得到余数进行校验码判断
     *
     * @param
     * @param sum17
     * @return 校验位
     */
    private static String getCheckCodeBySum(int sum17) {
        String checkCode = null;
        switch (sum17 % 11) {
            case 10:
                checkCode = "2";
                break;
            case 9:
                checkCode = "3";
                break;
            case 8:
                checkCode = "4";
                break;
            case 7:
                checkCode = "5";
                break;
            case 6:
                checkCode = "6";
                break;
            case 5:
                checkCode = "7";
                break;
            case 4:
                checkCode = "8";
                break;
            case 3:
                checkCode = "9";
                break;
            case 2:
                checkCode = "x";
                break;
            case 1:
                checkCode = "0";
                break;
            case 0:
                checkCode = "1";
                break;
        }
        return checkCode;
    }

    // 每位加权因子
    private static int power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

    /**
     * 截取字符串，不能超过限定的长度
     *
     * @param value
     * @return
     */
    public static String truncateString2(String value, int maxLength) {
        if (null != value) {
            if (maxLength < value.length()) {
                return value.substring(0, maxLength);
            }
        }
        return value;

    }

    /**
     * 转换手机号
     *
     * @return
     */
    public static String converterMobile(String mobile) {
        if (mobile == null || mobile.length() < 11) {
            return "";
        }
        return mobile.replace(mobile.substring(3, 7), "****");
    }

    /**
     * 性别 0未知1男2女
     *
     * @param flag
     * @return
     */
    public static String converterGender(int flag) {
        if (flag == 1) {
            return "先生";
        } else if (flag == 2) {
            return "女士";
        } else {
            return "";
        }
    }

    /**
     * 人的称谓
     *
     * 司机填写了称谓，没有填写姓名，提示“先生、女士”； 司机填写了称谓和姓名，提示“王先生、王女士”； 司机填写了姓名，没有填写称谓，不提示；
     * 司机没有填写姓名，没有填写称谓，不提示；

     * @return
     */
    public static String converterPersonTitle(String name_gender) {
        try {
            if (name_gender == null || "".equals(name_gender.trim())) {
                return "";
            }
            String[] str = name_gender.split("-");
            if (str.length == 0) {
                return "";
            }
            if (str.length == 1) {
                return converterGender(Integer.parseInt(str[0]));
            }
            if ("".equals(converterGender(Integer.parseInt(str[1])))) {
                return "";
            }
            if (str[0].length() > 1) {
                return str[0].substring(0, 1) + converterGender(Integer.parseInt(str[1]));
            } else {
                return str[0] + converterGender(Integer.parseInt(str[1]));
            }
        } catch (Exception e) {
            return "";
        }

    }

    /**
     * 根据编号，长度要求，前面补零 超长截取最后length
     *
     * @param no
     * @param length
     * @return
     */
    public static String getNo(String no, int length) {
        StringBuffer sb = new StringBuffer();
        int zeroNum = length - no.length();
        if (zeroNum >= 0) {
            for (int i = 0; i < zeroNum; i++) {
                sb.append(0);
            }
            return sb.append(no).toString();
        } else {
            return no.substring(no.length() - length, no.length());
        }

    }

    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     * 返回由对象的属性为key,值为map的value的Map集合
     *
     * @param obj
     *            Object
     * @return mapValue Map<String,String>
     * @throws Exception
     */
    public static Map<String, String> getFieldVlaue(Object obj) throws Exception {
        Map<String, String> mapValue = new HashMap<String, String>();
        Class<?> cls = obj.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();
            String strGet = "get" + name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
            Method methodGet = cls.getDeclaredMethod(strGet);
            Object object = methodGet.invoke(obj);
            String value = object != null ? object.toString() : "";
            mapValue.put(name, value);
        }
        return mapValue;
    }


    public static String addZeroForNum(String str, int strLength,int type) {
        int strLen = str.length();
        StringBuffer sb = null;
        while (strLen < strLength) {
            sb = new StringBuffer();
            if(type == 0)
                sb.append("0").append(str);// 左补0
            else
                sb.append(str).append("0");//右补0
            str = sb.toString();
            strLen = str.length();
        }
        return str;
    }

    /*将长整型数字id,用0在左侧补齐number位*/
    public static String addLeftZero(Long id,int number){
        NumberFormat formatter = NumberFormat.getNumberInstance();
        formatter.setMinimumIntegerDigits(number);
        formatter.setGroupingUsed(false);
        String s = formatter.format(id);
        return s;
    }
}
